package com.zhao.tool;

import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.util.*;

/**
 * @Author:zhao
 * @Date: 2018/7/31 15:29
 * @Description: XML实体代码生成器, 根据XML文件生成相应.java实体类
 */
@Slf4j
@SuppressWarnings("all")
public class ClassGeneratorTool {

    public static void main(String args[]) {
        ClassGeneratorTool main = new ClassGeneratorTool();
        main.operate();
    }

    //xml文件名
    private static final String XML_FILE_NAME = "generator.xml";

    //xml实体类目录
    private static final String BEAN_DIRECTORY = "C:\\Users\\zhao\\Desktop\\bean";

    //package
    private static final String PACKAGE = "com.zhao.pojo.message";

    //xml节点集合
    private static final List<Element> ELEMENT_LIST = new ArrayList<>();

    /**
     * 功能描述: 处理报文入口方法
     *
     * @param:
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 15:53
     */
    public void operate() {
        try {
            File file = ResourceUtils.getFile(String.join("/", "classpath:generator", XML_FILE_NAME));
            SAXReader reader = new SAXReader();
            Document document = reader.read(file);
            Element root = document.getRootElement();
            recursive(root);
            if (isNotEmpty(ELEMENT_LIST)) {
                for (Element element : ELEMENT_LIST) {
                    parse(element);
                }
            }
        } catch (Exception e) {
            log.error("报文处理失败:{}", e);
        }
    }

    /**
     * 功能描述:递归获取报文非叶子节点集合
     *
     * @param: element dom节点
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 15:55
     */
    private void recursive(Element element) {
        if (isLeafNode(element)) {
            return;
        }
        ELEMENT_LIST.add(element);
        Iterator iterator = element.elementIterator();
        while (iterator.hasNext()) {
            recursive((Element) iterator.next());
        }
    }

    /**
     * 功能描述: 具体节点解析
     *
     * @param: element dom节点
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 16:13
     */
    private void parse(Element element) {
        if (isLeafNode(element)) {
            return;
        }
        StringBuffer buffer = new StringBuffer();
        buffer.append("package ").append(PACKAGE).append(";");
        buffer.append("\n\n").append("import lombok.Data;");
        buffer.append("\n").append("import lombok.ToString;");
        buffer.append("\n").append("import javax.xml.bind.annotation.*;");

        buffer.append("\n\n").append("@Data");
        buffer.append("\n").append("@ToString");
        buffer.append("\n").append("@XmlRootElement(name=\"").append(element.getName()).append("\")");
        buffer.append("\n").append("@XmlAccessorType(XmlAccessType.FIELD)");
        buffer.append("\n").append("public class ").append(getClassName(element.getPath())).append(" {");
        //子节点
        List<Element> elements = element.elements();
        //子节点全路径集合
        List<String> nodePathList = new ArrayList<>();
        for (Element item : elements) {
            nodePathList.add(item.getPath());
        }
        //子节点计数
        Map<String, Integer> nodePathMap = count(nodePathList);
        for (Element item : elements) {
            Integer count=nodePathMap.get(item.getPath());
            if (count==null){
                continue;
            }
            nodePathMap.remove(item.getPath());
            if (isLeafNode(item)) {
                if (count > 1) {
                    //多个
                    buffer.append("\n\n\t").append("@XmlElements(value={@XmlElement(name=\"").append(item.getName()).append("\",type=String.class)})");
                    buffer.append("\n\t").append("private List<String> ").append(getFieldName(item.getPath())).append(";");
                } else {
                    //一个
                    buffer.append("\n\n\t").append("@XmlElement(name=\"").append(item.getName()).append("\")");
                    buffer.append("\n\t").append("private String ").append(getFieldName(item.getPath())).append(";");
                }
                continue;
            }
            if (count > 1) {
                //多个
                buffer.append("\n\n\t").append("@XmlElements(value={@XmlElement(name=\"").append(item.getName()).append("\",type=").append(getClassName(item.getPath())).append(".class)})");
                buffer.append("\n\t").append("private List<").append(getClassName(item.getPath())).append("> ").append(getFieldName(item.getPath())).append(";");
            } else {
                //一个
                buffer.append("\n\n\t").append("@XmlElement(name=\"").append(item.getName()).append("\")");
                buffer.append("\n\t").append("private ").append(getClassName(item.getPath())).append(" ").append(getFieldName(item.getPath())).append(";");
            }

        }
        buffer.append("\n\n}");
        try {
            write(getClassName(element.getPath()), buffer.toString());
        } catch (Exception e) {
            log.error("写文件失败:{}", e);
        }

    }

    /**
     * 功能描述: 写文件
     *
     * @param: className 类名
     * @param: body 文件内容
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 17:26
     */
    private void write(String className, String body) throws Exception {
        File directory = new File(BEAN_DIRECTORY);
        if (!directory.exists()) {
            directory.mkdirs();
        }
        File file = new File(directory.getAbsolutePath(), className + ".java");
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        fileOutputStream.write(body.getBytes());
        fileOutputStream.flush();
        fileOutputStream.close();
    }

    /**
     * 功能描述: 判断节点是否是叶子节点
     *
     * @param: element dom节点
     * @return: true or false
     * @auther: zhao
     * @date: 2018/7/31 15:59
     */
    private boolean isLeafNode(Element element) {
        List<Element> elements = element.elements();
        if (isEmpty(elements)) {
            return true;
        }
        return false;
    }

    /**
     * 功能描述: List为null或空
     *
     * @param: list 集合
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 16:04
     */
    private boolean isEmpty(List<?> list) {
        return list == null || list.isEmpty();
    }

    /**
     * 功能描述: List不为null或空
     *
     * @param: list 集合
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 16:04
     */
    private boolean isNotEmpty(List<?> list) {
        return !isEmpty(list);
    }

    /**
     * 功能描述: 根据xml全路径获取类名
     *
     * @param: str 节点全路径
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 16:29
     */
    private String getClassName(String str) {
        return parsePath(str, false);
    }

    /**
     * 功能描述: 根据xml全路径获取属性名
     *
     * @param: str 节点全路径
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 16:30
     */
    private String getFieldName(String str) {
        return parsePath(str, true);
    }

    /**
     * 功能描述: 解析xml全路径并返回路径字符串
     *
     * @param: str 节点全路径
     * @param: lowerCase 首字母是否大小写
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 16:27
     */
    private String parsePath(String str, boolean lowerCase) {
        String path = str.replace("/*[name()=", "").replace("]", "").replace("''", ",").replace("'", "").replace("/", ",");
        if (path.startsWith(",")) {
            path = path.substring(1);
        }
        String[] paths = path.split(",");
        String field = "";
        for (int i = 0; i < paths.length; i++) {
            field += paths[i].substring(0, 1).toUpperCase() + paths[i].substring(1);
        }
        if (lowerCase && !field.isEmpty()) {
            field = field.substring(0, 1).toLowerCase() + field.substring(1);
        }
        return field;
    }

    /**
     * 功能描述: 字符串集合计数
     *
     * @param: list 字符串集合
     * @return:
     * @auther: zhao
     * @date: 2018/7/31 16:41
     */
    private Map<String, Integer> count(List<String> list) {
        if (isEmpty(list)) {
            return null;
        }
        Map<String, Integer> map = new HashMap<>();
        for (String item : list) {
            Integer count = map.get(item);
            map.put(item, count != null && count > 0 ? (count + 1) : 1);
        }
        return map;
    }
}
